﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CPF_experement
{
    class SuperTreeNode
    {
        GDD[] allMDDs;
        HashSet<PoinForNewAlgoritem> closedList;
        int maxCost;
        AgentState[] startingPos;
        public int[] costs;
        LinkedList<PoinForNewAlgoritem>[] ans;

        public SuperTreeNode(AgentState[] startingPos, CostsSet costs) //make sure agents numbers are in the corect order
        {
            AgentState agent;
            maxCost=0;
            allMDDs=new GDD[startingPos.Length];
            this.startingPos = startingPos;
            this.costs = costs.costs;

            for (int i = 0; i < costs.costs.Length; i++) 
			{
                if (costs.costs[i]>maxCost)
                    maxCost = costs.costs[i];
			}
            for (int i = 0; i < startingPos.Length; i++) 
			{
                agent=startingPos[i];
			    allMDDs[i]=new GDD(i ,agent.agent.agentNum,agent.pos_X,agent.pos_Y,costs.costs[i],maxCost,startingPos.Length);
			}
        }

        public LinkedList<PoinForNewAlgoritem>[] solve()
        {
            bool sync;
            for (int i = allMDDs.Length-2; i >= 0; i--)
            {
                for (int j = i+1; j < allMDDs.Length; j++)
                {
                    //SuperTree.watchSyncronize.Start();
                    sync = allMDDs[i].syncGDDs(allMDDs[j], j);
                   // SuperTree.watchSyncronize.Stop();
                    if (!sync)
                    {
                        return null;
                    }
                    //printAllGDDs();
                }
            }
            closedList = new HashSet<PoinForNewAlgoritem>();
            ans = new LinkedList<PoinForNewAlgoritem>[allMDDs.Length];
            for (int i = 0; i < ans.Length; i++)
            {
                ans[i] = new LinkedList<PoinForNewAlgoritem>();
            }
           // SuperTree.numOfFindPath++;//////////////
           // SuperTree.watchBruteForce.Start();/////////////////
            bool success = chooseAndPrintSolution(0);
           // SuperTree.watchBruteForce.Stop();///////////////
            if (success)
            {
                return ans;
            }
            return null;
        }

        public bool chooseAndPrintSolution(int agentNum)
        {
            
            if (agentNum==allMDDs.Length)
            {
                return true;
            }
            return findPath(allMDDs[agentNum].levels[0].First.Value, agentNum);
        }

        public bool findPath(MDDNode from, int agentNum)
        {
            if (Run.watch.ElapsedMilliseconds > 300000)
            {
                return false;
            }
            if (from.level == maxCost)
            {
                return chooseAndPrintSolution(agentNum + 1);
            }
            foreach (MDDNode to in from.children)
            {
                int direction = PoinForNewAlgoritem.getDirection(to.getX(), to.getY(), from.getX(), from.getY());
                PoinForNewAlgoritem nextPoint = new PoinForNewAlgoritem(to.getX(), to.getY(), direction, to.level);
                if (!closedList.Contains(nextPoint.copyPointNoDirection()) && !closedList.Contains(nextPoint.getColaidingPoint()))
                {
                    closedList.Add(nextPoint);
                    LinkedListNode<PoinForNewAlgoritem> point = new LinkedListNode<PoinForNewAlgoritem>(nextPoint);
                    ans[agentNum].AddLast(point);
                    if (findPath(to,agentNum))
                    {
                        return true;
                    }
                    ans[agentNum].Remove(point);
                    closedList.Remove(nextPoint);
                }
            }
            
            return false;
        }

        public void expandNode(LinkedList<CostsSet> openList, HashSet<CostsSet> closedList)
        {
            CostsSet temp;
            for (int j = 0; j < costs.Length; j++)
            {
                int[] newCosts=new int[costs.Length];
                for (int i = 0; i < costs.Length; i++)
                {
                    newCosts[i]=costs[i];
                }
                newCosts[j]++;
                temp = new CostsSet(newCosts);
                if (!closedList.Contains(temp))
                {
                    closedList.Add(temp);
                    openList.AddLast(temp);
                }
            } 
        }
        public void printAllGDDs()
        {
            Console.WriteLine("*********************************************************************************");
            foreach (GDD toPrin in allMDDs)
            {
                Console.WriteLine("\n\n\n-----------------------\nfor agent num - " + toPrin.getAgentNum()+"     *");
                Console.WriteLine("-----------------------");
                toPrin.printGDD();
                Console.ReadLine();
            }
        }
    }
    class CostsSet
    {
        public int[] costs;

        public CostsSet(int[] costs)
        {
            this.costs = costs;
        }
        public override int GetHashCode()
        {
            int ans = 0;
            int[] prime = { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 71, 73, 79 };
            for (int i = 0; i < costs.Length; i++)
            {
                ans += costs[i] * prime[i % 22];
            }
            return ans%10000;
        }
        public override bool Equals(object obj)
        {
            CostsSet check = (CostsSet)obj;
            for (int i = 0; i < costs.Length; i++)
            {
                if (costs[i]!=check.costs[i])
                {
                    return false;
                }
            }
            return true;
        }
    }
}
